package com.psak.cudaphrasal.client.task.processing;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import com.psak.cudaphrasal.model.FileChunkToProcess;
import com.psak.cudaphrasal.model.ProcessingResult;
import com.psak.cudaphrasal.model.SinglePhrasalResult;

/**
 * Usluga uruchamiajaca przetwarzania procesu przetwarzania chunka.
 * 
 * @author piotrs10
 * 
 */
public class TaskProcessingService {

	private static final String RESULT_FILE = "result.file";
	private static final String INPUT_FILE = "input.file";
	private static Logger LOG = Logger.getLogger(TaskProcessingService.class);

	public ProcessingResult processChunk(FileChunkToProcess chunk,
			String processPath) throws TaskProcessingException {
		ProcessBuilder processBuilder = new ProcessBuilder(processPath);
		File processingDirectory = new File(processPath).getParentFile();
		File inputFile = null;
		try {
			// zapisujemy do pliku wejsciowego
			inputFile = writeToFile(chunk, processingDirectory);
			// ustawiamy directory
			processBuilder.directory(processingDirectory);
			// uruchamiamy proces
			Process process = processBuilder.start();
			process.waitFor();

			int exitValue = process.exitValue();
			if (exitValue == 0) {
				// jezeli proces zwrocil 0 wyciagamy wyniki
				return extractResults(processingDirectory);
			} else {
				// w przypadku bledu wyciagamy komunikat i konczymy
				// przetwarzanie
				String errorMessage = extractErrors(process.getErrorStream());
				throw new TaskProcessingException(
						"Blad przetwarzania, proces zwrocil: " + errorMessage);
			}
		} catch (IOException e) {
			throw new TaskProcessingException("Blad uruchomienia procesu", e);
		} catch (InterruptedException e) {
			throw new TaskProcessingException(
					"Przerwano watek oczekiwania na zakonczenie procesu", e);
		} finally {
			if (inputFile != null && inputFile.exists()) {
				inputFile.delete();
			}
		}
	}

	private File writeToFile(FileChunkToProcess chunk, File processingDirectory)
			throws TaskProcessingException {
		File inputFile = new File(processingDirectory.getAbsolutePath()
				+ File.separator + INPUT_FILE);
		if (inputFile.exists()) {
			inputFile.delete();
			try {
				inputFile.createNewFile();
			} catch (IOException e) {
				throw new TaskProcessingException("Nie mozna stworzyc pliku", e);
			}
		}
		BufferedWriter bufferedWriter = null;
		try {
			bufferedWriter = new BufferedWriter(new FileWriter(inputFile));
			for (Long currentHash : chunk.getChunkWordHashList()) {
				bufferedWriter.write(String.valueOf(currentHash));
				bufferedWriter.write(";");

			}
		} catch (IOException e) {
			throw new TaskProcessingException("Blad zapisu pliku wejsciowego",
					e);
		} finally {
			if (bufferedWriter != null) {
				try {
					bufferedWriter.close();
				} catch (IOException e) {
					throw new TaskProcessingException(
							"Nie mozna zamknac pliku", e);
				}
			}
		}
		return inputFile;
	}

	private String extractErrors(InputStream errorStream) throws IOException {
		StringBuilder errorStringBuilder = new StringBuilder();
		BufferedReader bufferedReader = null;
		String result = null;
		try {
			bufferedReader = new BufferedReader(new InputStreamReader(
					errorStream), 8192);
			while ((result = bufferedReader.readLine()) != null) {
				errorStringBuilder.append(result);
			}
		} finally {
			if (bufferedReader != null) {
				bufferedReader.close();
			}
		}
		return errorStringBuilder.toString();
	}

	private ProcessingResult extractResults(File processingDirectory)
			throws TaskProcessingException {
		String resultFileName = RESULT_FILE;
		File file = new File(processingDirectory.getAbsolutePath()
				+ File.separator + resultFileName);
		if (!file.exists()) {
			throw new TaskProcessingException("Brak pliku wynikowego procesu");
		}

		BufferedReader bufferedReader = null;
		String result = null;
		ProcessingResult processingResult = new ProcessingResult();
		processingResult
				.setPhrasalResults(new ArrayList<SinglePhrasalResult>());
		try {
			bufferedReader = new BufferedReader(new InputStreamReader(
					new FileInputStream(file)), 8192);
			while ((result = bufferedReader.readLine()) != null) {
				String[] resultTokenParts = result.split(";");
				if (resultTokenParts.length != 2) {
					throw new TaskProcessingException(
							"Nieprawidlowy format pliku wynikowego procesu");
				}
				String phrasalPart = resultTokenParts[0].trim();
				String numOfOccurencesPart = resultTokenParts[1].trim();
				SinglePhrasalResult singlePhrasalResult = extractSingleResult(
						phrasalPart, numOfOccurencesPart);
				processingResult.getPhrasalResults().add(singlePhrasalResult);
			}
		} catch (FileNotFoundException e) {
			throw new TaskProcessingException("Brak pliku wynikowego procesu");
		} catch (IOException e) {
			throw new TaskProcessingException("Blad odczytu z pliku wynikowego");
		} finally {
			if (bufferedReader != null) {
				try {
					bufferedReader.close();
				} catch (IOException e) {
					throw new TaskProcessingException(
							"Nie mozna zamknac pliku wynikowego");
				}
			}
			if (file != null && file.exists()) {
				file.delete();
			}
		}

		return processingResult;
	}

	private SinglePhrasalResult extractSingleResult(String phrasalPart,
			String numOfOccurencesPart) throws TaskProcessingException {
		SinglePhrasalResult singlePhrasalResult = new SinglePhrasalResult();
		List<Long> phrasalHashList = new ArrayList<Long>();
		String[] wordHashString = phrasalPart.split(",");
		for (String wordHash : wordHashString) {
			try {
				Long hash = Long.valueOf(wordHash);
				phrasalHashList.add(hash);
			} catch (NumberFormatException e) {
				long dummyHash = 10;
				phrasalHashList.add(dummyHash);
			}
		}
		singlePhrasalResult.setPhrasalHash(phrasalHashList);
		try {
			long numOfOccurences = Long.valueOf(numOfOccurencesPart);
			singlePhrasalResult.setNumberOfOccurences(numOfOccurences);
		} catch (NumberFormatException e) {
			long dummyNumber = 0;
			singlePhrasalResult.setNumberOfOccurences(dummyNumber);
		}
		return singlePhrasalResult;
	}

}
